#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unistd.h>
#include <string>
#include <pthread.h>
#include <cstring>
#include "Protocol.hpp"
#define NUM 1024
using namespace std;
typedef function<bool(const httpRequest &, httpResponse &)> func_t; // 回调函数，用来处理业务逻辑

static const uint16_t gport = 8080;
static const int gbacklog = 5; // 这里暂时不需要了解
enum
{
    USAGE_ERROR = 1,
    SOCK_ERROR,
    BIND_ERROR,
    LISTEN_ERROR
};

class httpserver
{
public:
    httpserver(func_t func,const uint16_t &port = gport)
        : _port(port), _listenfd(-1),_func(func)
    {
    }
    void initServer()
    {
        // 1.创建tcp套接字
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            exit(SOCK_ERROR);
        }
        //1.1设置地址复用，防止再次绑定时绑定失败
        int opt = 1;
        setsockopt(_listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
        // 绑定bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        if (bind(_listenfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            exit(BIND_ERROR);
        }
        // 监听
        if (listen(_listenfd, gbacklog) != 0)
        {
            exit(LISTEN_ERROR);
        }
    }
    void Handlerhttp(int sock)
    {
        // 1.接受信息
        // 2.反序列化
        // 3.回调上层调用的方法
        // 4.序列化
        // 5.发送数据给客户端
        char buffer[4096];
        httpRequest req;
        httpResponse resp;
        //我们这里直接接受数据，这里不考虑接收不全的情况
        ssize_t n = recv(sock,buffer,sizeof(buffer)-1,0);
        if(n > 0)
        {
            buffer[n] = 0;
            req.inbuffer = buffer;
            req.prase();
            _func(req,resp);
            send(sock,resp.outbuffer.c_str(),resp.outbuffer.size(),0);
        }
    }

    void start()
    {
        signal(SIGCHLD, SIG_IGN); // 让父进程不需要等待子进程。
        for (;;)
        {
            // 连接,注意这里连接之后是创建新的文件描述符，和listenfd是不相同的！
            struct sockaddr_in client; // 这里要连接的client
            socklen_t len = sizeof(client);
            int sock = accept(_listenfd, (struct sockaddr *)&client, &len);
            // 这里的创建出新的文件描述符，可以直接用于通信，这里本质就是文件操作
            if (sock < 0)
            {
                continue; // 这里创建失败可以再次创建，不是严重的错误
            }
            // version2 多线程
            pid_t id = fork();
            if (id == 0)
            {
                // 子进程
                // 关闭父进程的listenfd，因为子进程不需要完成监听工作
                close(_listenfd);
                if(fork()>0) exit(0); // 这里让子进程退出，让孙子进程完成任务，完成任务后会被OS领养
                // 序列化，反序列化，以及报头的增减
                Handlerhttp(sock);
                close(sock);
                exit(0);
            }
            // 每次使用完都关闭，否者会导致文件描述符越用越少。
            close(sock); // 这里父进程可以关闭，因为这里的关闭并不是真正的关闭而是引用计数--。
            // 等待子进程，这里可以通过设置信号解决，但是也可以通过孤儿进程被OS领养来解决
        }
    }

    ~httpserver()
    {
    }

private:
    uint16_t _port; // 端口号
    int _listenfd;  // 监听套接字
    func_t _func; // 上层调用的回调函数
};